#include "stdafx.h"
#include "Win32Device.h"
#include "config/Def.h"
#include "config/DeviceCon.h"
#include "config/IncludeParts.h"
#include "UniGame.h"
#include "../Input.h"

using namespace unigame;

LRESULT CALLBACK UniWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

Win32Device::Win32Device():
Device(),
m_hdc(0),
m_hinstance(0),
m_hwnd(0),
m_hasDisplay(false)
{
	m_resourcePathLength = 5;
	m_resourcePath = uni_new char[m_resourcePathLength + 1];
	sprintf(m_resourcePath, "%s", "data/");
	m_savePathLength = 5;
	m_savePath = uni_new char[m_savePathLength + 1];
	sprintf(m_savePath, "%s", "save/");
	m_screenSize.x = SCREEN_WIDTH;
	m_screenSize.y = SCREEN_HEIGHT;
}

Win32Device::~Win32Device()
{
	Render::instance()->deleteContext();
	ReleaseDC(m_hwnd, m_hdc);
	m_hasDisplay = false;
	if(m_isFullScreen)
	{
		ChangeDisplaySettings(0, 0);
	}
}

void Win32Device::startDisplay()
{
	registerClass();
	createWindow();
	createHDC();
	ShowWindow(m_hwnd, SW_SHOW);
	UpdateWindow(m_hwnd);
	m_hasDisplay = true;
}

void Win32Device::setParam(int paramID, void* param)
{
	switch(paramID)
	{
	case E_WND_INSTANCE:
		m_hinstance = (HINSTANCE)param;
		break;
	default:
		break;
	}
}

void Win32Device::isFullScreen(bool isFull)
{
	if(!m_hasDisplay)
	{
		m_isFullScreen = isFull;
	}
}

void Win32Device::registerClass()
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = UniWndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = m_hinstance;
	wcex.hIcon = LoadIcon(m_hinstance, IDI_WINLOGO);
	wcex.hCursor = LoadCursor(0, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName = 0;
	wcex.lpszClassName = TITLE;
	wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_WINLOGO);

	ATOM ret = RegisterClassEx(&wcex);
	assert(ret && "registe wnd class fail");
}

void Win32Device::createWindow()
{
	int w = GetSystemMetrics(SM_CXSCREEN);
	int x = 0;
	int h = GetSystemMetrics(SM_CYSCREEN);
	int y = 0;
	DWORD exStyle = WS_EX_APPWINDOW;
	DWORD style = WS_POPUP;
	if(m_isFullScreen)
	{
		DEVMODE dm;
		memset(&dm, 0, sizeof(dm));
		dm.dmSize = sizeof(dm);
		dm.dmPelsWidth = w;
		dm.dmPelsHeight = h;
		dm.dmBitsPerPel = 32;
		dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
		int ret = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
		if(ret != DISP_CHANGE_SUCCESSFUL)
		{
			m_isFullScreen = false;
			Debug::log("full screen change failed");
		}
	}
	if(!m_isFullScreen)
	{
		x =  (w - SCREEN_WIDTH) / 2;
		y = (h - SCREEN_HEIGHT) / 2;
		w = SCREEN_WIDTH;
		h = SCREEN_HEIGHT;
		exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		style = WS_OVERLAPPEDWINDOW;
	}
	RECT rc = {0, 0, w, h};
	AdjustWindowRectEx(&rc, style, false, exStyle);
	w = rc.right - rc.left;
	h = rc.bottom - rc.top;
	m_hwnd = CreateWindowEx(exStyle, TITLE, TITLE, style, x, y, w, h, 0, 0, m_hinstance, 0);
	assert(m_hwnd && "create window fail");
}

void Win32Device::createHDC()
{
	m_hdc = GetDC(m_hwnd);
	Render::instance()->setContext(m_hdc);
}

int buttonPress = 0;
void mouseEvent(UINT message, WPARAM wp, LPARAM lp)
{
	MouseMove* mm = Input::instance()->mouse();
	float x = LOWORD(lp);
	float y = HIWORD(lp);
	int index = 0;
	mm[E_NONE_MB].m_position.x = x;
	mm[E_NONE_MB].m_position.y = y;
	bool isDown = false;
	switch(message)
	{
	case WM_LBUTTONDOWN:
		index = E_LEFT_MB;
		isDown = true;
		break;
	case WM_LBUTTONUP:
		index = E_LEFT_MB;
		isDown = false;
		break;
	case WM_RBUTTONDOWN:
		index = E_RIGHT_MB;
		isDown = true;
		break;
	case WM_RBUTTONUP:
		index = E_RIGHT_MB;
		isDown = false;
		break;
	case WM_MBUTTONDOWN:
		index = E_MIDDLE_MB;
		isDown = true;
		break;
	case WM_MBUTTONUP:
		index = E_MIDDLE_MB;
		isDown = false;
		break;
	case WM_MOUSEMOVE:
		if(buttonPress > 0)
		{
			for(int i = 1; i < E_COUNT_MB; ++i)
			{
				if((mm[i].m_flag & E_PRESSING_IA) > 0)
				{
					mm[i].m_flag |= E_DRAG_IA;
					mm[i].m_position.x = x;
					mm[i].m_position.y = y;
				}
			}
		}
		mm[E_NONE_MB].m_flag |= E_MOVE_IA;
		return;
	default:
		return;
	}
	if(isDown)
	{
		mm[index].m_flag |= E_PRESS_IA;
		mm[index].m_flag |= E_PRESSING_IA;
		mm[index].m_start.x = x;
		mm[index].m_start.y = y;
		mm[index].m_position.x = x;
		mm[index].m_position.y = y;
		buttonPress |= 1 << index;
	}
	else
	{
		mm[index].m_flag |= E_RELEASE_IA;
		mm[index].m_flag &= ~E_PRESSING_IA;
		mm[index].m_flag &= ~E_DRAG_IA;
		mm[index].m_position.x = x;
		mm[index].m_position.y = y;
		buttonPress &= ~(1 << index);
	}
}

void keyBoardEvent(bool isDown, WPARAM wp)
{
	int index = 0;
	if(wp >= VK_F1 && wp <= VK_F12)
	{
		index = E_F1_KB + wp - VK_F1;
	}
	else if(wp >= 0x30 && wp <= 0x39)
	{
		index = E_0_KB + wp - 0x30;
	}
	else if(wp >= 0x41 && wp <= 0x5A)
	{
		index = E_A_KB + wp - 0x41;
	}
	else
	{
		switch(wp)
		{
		case VK_BACK:
			index = E_BACK_SPACE_KB;
			break;
		case VK_ADD:
			index = E_ADD_KB;
			break;
		case VK_SUBTRACT:
			index = E_SUB_KB;
			break;
		case VK_RETURN:
			index = E_ENTER_KB;
			break;
		case VK_LSHIFT:
			index = E_L_SHIFT_KB;
			break;
		case VK_RSHIFT:
			index = E_R_SHIFT_KB;
			break;
		case VK_LCONTROL:
			index = E_L_CTRL_KB;
			break;
		case VK_RCONTROL:
			index = E_R_CTRL_KB;
			break;
		case VK_SPACE:
			index = E_SPACE_KB;
			break;
		case VK_TAB:
			index = E_TAB_KB;
			break;
		case VK_CAPITAL:
			index = E_CAP_KB;
			break;
		case VK_LWIN:
			index = E_L_WIN_KB;
			break;
		case VK_RWIN:
			index = E_R_WIN_KB;
			break;
		case VK_APPS:
			index = E_APP_KB;
			break;
		case VK_HOME:
			index = E_HOME_KB;
			break;
		case VK_END:
			index = E_END_KB;
			break;
		case VK_DELETE:
			index = E_DEL_KB;
			break;
		case VK_PRIOR:
			index = E_PAGE_UP_KB;
			break;
		case VK_NEXT:
			index = E_PAGE_DOWN_KB;
			break;
		case VK_LEFT:
			index = E_LEFT_KB;
			break;
		case VK_RIGHT:
			index = E_RIGHT_KB;
			break;
		case VK_UP:
			index = E_UP_KB;
			break;
		case VK_DOWN:
			index = E_DOWN_KB;
			break;
		case VK_OEM_COMMA:
			index = E_COMMA_KB;
			break;
		case VK_OEM_PERIOD:
			index = E_PERIOD_KB;
			break;
		case VK_OEM_2:
			index = E_QUES_KB;
			break;
		case VK_OEM_3:
			index = E_DOT_KB;
			break;
		case VK_OEM_1:
			index = E_COLON_KB;
			break;
		case VK_OEM_7:
			index = E_QUOT_KB;
			break;
		case VK_OEM_4:
			index = E_L_SB_KB;
			break;
		case VK_OEM_6:
			index = E_R_SB_KB;
			break;
		case VK_OEM_5:
			index = E_SLASH_KB;
			break;
		case VK_LMENU:
			index = E_L_ALT_KB;
			break;
		case VK_RMENU:
			index = E_R_ALT_KB;
			break;
		default:
			return;
		}
	}
	char* kb = Input::instance()->keyButtonFlag();
	if(isDown)
	{
		if((E_PRESSING_IA & kb[index]) == 0)
		{
			kb[index] |= E_PRESSING_IA;
			kb[index] |= E_PRESS_IA;
		}
	}
	else
	{
		kb[index] |= E_RELEASE_IA;
		kb[index] &= ~E_PRESSING_IA;
	}
}

LRESULT CALLBACK UniWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		break;
	case WM_DESTROY:
		UniGame::destroy();
		PostQuitMessage(0);
		break;
	case WM_MBUTTONDOWN:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
	case WM_MOUSEMOVE:
		mouseEvent(message, wParam, lParam);
		break;
	case WM_KEYDOWN:
		keyBoardEvent(true, wParam);
		break;
	case WM_KEYUP:
		keyBoardEvent(false, wParam);
		break;
	case WM_SIZE:
		Device::currentDevice()->resizeScreen(LOWORD(lParam), HIWORD(lParam));
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}